﻿#region

using System;
using System.Collections;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Diagnostics;
using System.Linq.Expressions;
using log4net;
using log4net.Repository.Hierarchy;

#endregion

namespace CNav.Repository.UoV
{
    public interface IUnitOfWork
    {
        void Dispose();
        void Commit();
        IRepository<T> Repository<T>() where T : class;
        IRepositoryQuery<T> Get<T>() where T : class;
        IRepositoryQuery<T> Get<T>(Expression<Func<T, bool>> filter) where T : class;
    }

    public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbContext context;
        private readonly IRepositoryFactory repositoryFactory;
        private bool disposed;
        private Hashtable repositories;

        private static readonly ILog Logger = LogManager.GetLogger(typeof(DataBaseContext));

        public UnitOfWork()
        {
#if DEBUG
            Debug.WriteLine("ctor: => UnitOfWork");
#endif
            this.context = new DataBaseContext();
            repositoryFactory = new RepositoryFactory(context);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Commit()
        {
            try
            {
                context.SaveChanges();
            }
            catch (DbEntityValidationException e)
            {
                foreach (var eve in e.EntityValidationErrors)
                {
                    Debug.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                        eve.Entry.Entity.GetType().Name, eve.Entry.State);
                    foreach (var ve in eve.ValidationErrors)
                    {
                        Debug.WriteLine("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage);
                    }
                }
                throw;
            }
            catch (DbUpdateException e)
            {
                foreach (var eve in e.Entries)
                {
                    Debug.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                        eve.Entity.GetType().Name, eve.State);

                    Debug.WriteLine("- OriginalValues: \"{0}\", Error: \"{1}\"", eve.CurrentValues, eve.CurrentValues);
                }
                throw;
            }
        }

        public virtual void Dispose(bool disposing)
        {
            if (!disposed)
                if (disposing)
                    context.Dispose();

            disposed = true;
        }

        public IRepositoryQuery<T> Get<T>() where T : class
        {
            var query = new RepositoryQuery<T>(EnsureRepository<T>());
            return query;
        }

        public IRepositoryQuery<T> Get<T>(Expression<Func<T, bool>> filter) where T : class
        {
            var query = new RepositoryQuery<T>(EnsureRepository<T>());
            query.Where(filter);
            return query;
        }

        public IRepository<T> Repository<T>() where T : class
        {
            return EnsureRepository<T>();
        }

        private Repository<T> EnsureRepository<T>() where T : class
        {
            if (repositories == null)
                repositories = new Hashtable();

            var type = typeof(T).Name;

            if (!repositories.ContainsKey(type))
            {
                repositories.Add(type, repositoryFactory.Get<T>());
            }
            return (Repository<T>)repositories[type];
        }
    }
}